home *** CD-ROM | disk | FTP | other *** search
/ PsL Monthly 1993 December / PSL Monthly Shareware CD-ROM (December 1993).iso / prgmming / dos / pascal / tpfort.com / TPFORT.DOC < prev   
Encoding:
Text File  |  1989-11-19  |  16.0 KB  |  373 lines

  1. TPFort v 1.0 - A link from Turbo Pascal to MS Fortran.  
  2.  
  3. Copyright (c) 1989 D.J. Murdoch.  All rights reserved.
  4.  
  5. PURPOSE
  6.  
  7. TPFort is a collection of several procedures that allow Microsoft Fortran 
  8. routines to be called from Turbo Pascal.  I wrote it so that I could use the 
  9. binary-only NAG Fortran library for numerical routines in Turbo Pascal 
  10. programs, but it ended up being a general purpose linker.
  11.  
  12. PRICE
  13.  
  14. TPFort is absolutely free to use and incorporate into your own programs for 
  15. any purpose.  Distribute it to anyone you like, but please don't remove my 
  16. copyright notice or modify it in any other way.  Source code is available 
  17. (see below), but is not necessary to be able to use TPFort with Turbo Pascal 
  18. version 5.5. 
  19.  
  20. METHOD 
  21.  
  22. The Fortran routines are compiled into their own loader file which is loaded at 
  23. run time by a Turbo Pascal program, making most of the Fortran subroutines and 
  24. functions available to the Pascal program.  The molasses-slow Fortran compiler 
  25. and linker need only be run once to create the loader; changes to the Pascal 
  26. part of the program don't force recompiling or re-linking of the Fortran part.
  27.  
  28. INSTRUCTIONS
  29.  
  30. There are several steps involved in preparing a Fortran routines to be called 
  31. from Turbo Pascal.  
  32.  
  33. 1.   Preparing the Fortran Program
  34.  
  35. Write a Fortran program which includes the following declarations and a call 
  36. to CALLTP, in the following format: 
  37.  
  38.         EXTERNAL routine1, routine2, ..., routineX
  39.  
  40.         CALL CALLTP(routine1, routine2, ..., routineX, X)
  41.  
  42. where routine1 through to routineX are the names of the Fortran routines you 
  43. wish to make available to your Turbo Pascal program, and X is an integer value
  44. giving the number of routines being passed.  The external declaration is 
  45. extremely important; if not given, Fortran will assume the routine names are 
  46. local integer or real variables, and things will get really messed up.
  47.  
  48. This loader may do anything else, such as reading data from files, allocating 
  49. space, etc.  It's not all that important where the call to CALLTP takes place, 
  50. but more efficient use will be made of the program stack if the call comes 
  51. somewhere in the main program, rather than in a function or subroutine. 
  52.  
  53. After this call and any other initialization necessary, the program should
  54. exit.  As this will close any open files, and I/O done while TP is active
  55. is probably unreliable, it should complete any I/O operations before quitting, 
  56. and the routines being passed should avoid doing I/O.
  57.  
  58. Compile this routine and link it to the object file CALLTP.OBJ.  Be sure to
  59. specify to the linker that a larger than normal stack will be needed - I'd
  60. suggest a minimum of 16K. The Turbo Pascal program will be using this stack 
  61. instead of its own much of the time, and TP makes much heavier use of the 
  62. stack than does Fortran.
  63.  
  64. Warning:  Don't try running the loader program on its own, unless you avoid 
  65. executing the call to CALLTP.  If TP isn't there to catch that call, you're 
  66. very likely to crash.  It might be a good idea to rename the .EXE with a non-
  67. executable extension such as .LDR just to be sure. 
  68.  
  69. 2.   Preparing the TP dummy procedures
  70.  
  71. You need to create dummy versions of all the Fortran routines that you want to 
  72. call.  They _must_ be declared as "far" routines, either through the use of 
  73. the $F+ compiler directive, or by putting them in the interface section of a 
  74. unit.  I'd suggest isolating all of them into their own unit and interfacing 
  75. them, but this isn't necessary. 
  76.  
  77. Each of the dummy routines takes an argument list that corresponds exactly to 
  78. the argument list of the Fortran routine.  By default, all Fortran arguments 
  79. are passed by reference, so these should be too, by declaring them as "var" 
  80. parameters.  The following list gives corresponding types between the two 
  81. languages:
  82.  
  83.   Fortran               TP
  84.  
  85.   INTEGER*2             integer
  86.   INTEGER*4             longint
  87.   INTEGER               longint
  88.   REAL                  single
  89.   REAL*4                single
  90.   REAL*8                double
  91.   DOUBLE PRECISION      double
  92.   CHARACTER*n           array[1..n] of char
  93.   COMPLEX               fort_complex8
  94.   COMPLEX*8             fort_complex8        These types will be declared in
  95.   COMPLEX*16            fort_complex16       the FortLink unit someday
  96.   LOGICAL               fort_logical 
  97.   EXTERNAL              (special - see note below)
  98.  
  99. Note also that Fortran and TP use different conventions for the order of
  100. indices in multi-dimensional arrays.  For example, the Fortran array
  101.  
  102.   REAL X(10,20,30)
  103.  
  104. would be declared as
  105.  
  106.   x : array[1..30,1..20,1..10] of single;
  107.  
  108. in TP. Note also that TP (up to version 5.5, at least) has no facility for 
  109. variable dimensions on arrays:  to handle an array which is declared as X(N,M)
  110. you have to declare X as a one-dimensional array and handle the indexing 
  111. yourself.
  112.  
  113. Thus a call to the NAG matrix inversion routine F01AAF with Fortran 
  114. declaration 
  115.  
  116.  SUBROUTINE F01AAF(A, IA, N, UNIT, IUNIT, WKSPCE, IFAIL)
  117.  INTEGER IA, N, IUNIT, IFAIL
  118.  REAL*8 A(IA,N), UNIT(IUNIT,N), WKSPCE(N)
  119.  
  120. would be simulated with dummy declarations something like
  121.  
  122.  procedure f01aaf(var a:realarray;       { realarray is declared in the 
  123.                                            FortLink unit }
  124.                   var ia, n:longint; 
  125.                   var unit:realarray;
  126.                   var iunit:longint;
  127.                   var wkspce:realarray;
  128.                   var ifail:longint);
  129.  
  130. and element A(I,J) would be addressed at a[i+(j-1)*ia].
  131.  
  132. The content of the dummy pascal routine is very simple, and should not be 
  133. varied.  If the Fortran routine is a SUBROUTINE, use a definition like
  134.   
  135.   const 
  136.     f01aaf_num = 1;  { this is the position of F01AAF in the call to CALLTP }
  137.  
  138.   procedure f01aaf;
  139.   begin
  140.     callfort(f01aaf_num);
  141.   end;
  142.  
  143. If desired, additional instructions can be put before the call to callfort;
  144. however, no local variables may be declared and no instructions may follow the 
  145. call.
  146.    
  147. If the Fortran routine is a REAL*8-valued FUNCTION, the declarations are 
  148. similar, except that the "fdouble" procedure replaces callfort.  For example, 
  149. for the Gaussian random number generator G05DDF, the Fortran declaration is 
  150.  
  151.  REAL*8 FUNCTION G05DDF(A, B)
  152.  REAL*8 A, B
  153.  
  154. and the Pascal declarations are
  155.  
  156.  function g05ddf(var a,b:double):double;
  157.  
  158. with implementation
  159.  
  160.  const g05ddf_num = 2;
  161.  function g05ddf;
  162.  begin
  163.    fdouble(g05ddf_num);   { Note that this is a procedure! }
  164.  end;
  165.  
  166.  
  167. 3.  Preparing the TP main program 
  168.  
  169. Once you have your dummy procedure unit set up, you have to make some 
  170. modifications to the main program to link in the Fortran at run-time.
  171. This is all done in a single call to 
  172.  
  173.  function loadfort(prog:string;TPentry:pointer):boolean;
  174.  
  175. The prog argument should contain a string giving the fully qualified name of 
  176. the Fortran program to be loaded; TPentry should give the address of a TP 
  177. routine taking no arguments, which is going to do all the calculations with 
  178. the Fortran routines.  It's necessary to do things this way because the call 
  179. to loadfort switches over to the Fortran stack; TPentry^ and any routine it 
  180. calls must be able to execute there.  If LoadFort is successful, it won't exit 
  181. until TPentry^ returns, and it'll give a True return value.  If it fails
  182. for some reason, it'll print a message and return a False value.  In this
  183. case TPEntry^ wasn't executed at all.  It's possible to call loadfort several 
  184. times if you want to switch in and out of "Fortran mode", though I don't know
  185. any reason to do so.  Only the first time will load anything, but they'll all 
  186. attempt to switch to Fortran mode.  Be sure never to call a Fortran 
  187. routine when you're not in Fortran mode, or you're likely to crash (or at 
  188. least get garbage out). To help determine the current status, the FortLink
  189. unit interfaces two variables:
  190.  
  191.   fortloaded     : boolean;    { True indicates Fortran routines are in memory }
  192.   fortsafe       : boolean;    { True indicates you're in Fortran mode }        
  193.  
  194. If you like, you can put tests of fortsafe into your dummy routines before
  195. the callfort or fdouble calls, to abort if there's a problem.
  196.  
  197. Warning:  the Turbo Pascal floating point emulator won't work in Fortran mode.
  198. You may be able to use the Fortran emulator with some extra programming; I 
  199. haven't tried.
  200.  
  201. PASSING FUNCTION REFERENCES
  202.  
  203. It is possible to pass function or procedure references to a Fortran routine, 
  204. but it's a little tricky.  
  205.  
  206. The Fortran setup is just the same as for any other kind of routine.  Just 
  207. pass the procedure name in CALLTP.
  208.  
  209. The dummy definition is much the same.  Declare the parameter which is the 
  210. routine being passed as type "extval", passed by value.  
  211.  
  212. The main routine then calculates the reference extval using a call to 
  213. "fort_external(procnum)", where procnum is the number of a Fortran procedure 
  214. being passed, or "pas_external(@proc)", where proc is the Pascal procedure 
  215. being passed, and saves the answer in a temporary variable.  It passes this
  216. variable to the dummy routine.
  217.  
  218. The main bug in this procedure is that fort_external and pas_external 
  219. allocate space for a pointer on the stack, and leave it there.  Thus you can't 
  220. execute them in the middle of an expression, or it will get messed up.  
  221. You should call the routine Clean_External as soon as possible after using 
  222. the temporary variable, to restore the stack to normal.  Call it once for each 
  223. call you made to fort_external or pas_external.  In a loop it's probably safe 
  224. to calculate the temporary once at the beginning and only clean it up once at 
  225. the end.  You MUST reassign the temporary variable every time you enter the 
  226. routine that uses it, because its value becomes worthless as soon as you call 
  227. Clean_external or exit. 
  228.  
  229. There's another bug in pas_external - the Pascal routine will be executed 
  230. fully in the Fortran context.  In particular, this means all global references 
  231. will reference the wrong data segment, and TP is likely to overwrite registers 
  232. that Fortran expects to have preserved.  To fix this up, at the very beginning 
  233. you must call Enter_Pascal, and you must call Leave_Pascal just before 
  234. exiting.  This temporarily restores the Pascal context, and saves some 
  235. registers. Note that stack checking has to be disabled in a routine being 
  236. passed this way, since the stack checker makes a reference to the global 
  237. System.Stacklimit, and gets executed before Enter_Pascal.  
  238.  
  239. Calls back to Fortran routines are allowed, but may not be recursive.  For 
  240. example, if the Fortran routine Minimizer gets passed the TP Myfunction to 
  241. minimize, then Myfunction can't call Minimizer, but it can call some other 
  242. Fortran routine.  This isn't such a large restriction though, because most 
  243. Fortran routines don't allow recursive calls anyways.  (Actually there's one 
  244. way around this:  pass the Fortran routine through CALLTP several times.  If 
  245. the Fortran routine could handle recursive calls normally, then the separate 
  246. dummy functions will be able to call each other.)
  247.  
  248. There's no way to call most TP functions, but it's possible to construct TP 
  249. functions which simulate any Fortran function.  The problem is that TP and 
  250. Fortran use very different conventions for passing function values back to the 
  251. caller.  TP usually returns them in registers, while a Fortran caller 
  252. allocates temporary space and expects the function to put the value there.  
  253. So, to write a TP routine that looks to Fortran as though it has the 
  254. declaration 
  255.  
  256.   REAL*8 FUNCTION SUMSQ(N,X)
  257.   INTEGER N
  258.   REAL*8 X(N)
  259.  
  260. write the header as follows: 
  261.  
  262.  function sumsq(var n:longint; var x:realarray; { Mimic the Fortran parameters 
  263.                                                   first }
  264.          value_ofs:word):double_ptr;     { Always add another word for the
  265.                                            return address, and return a 
  266.                                            pointer. "double_ptr" is a 
  267.                                            pointer to a double declared in 
  268.                                            FortLink }
  269.  
  270. See the sample program for the rest of the details.
  271.  
  272. EXAMPLE
  273.  
  274. A sample program is contained in the following files:
  275.  
  276.   PSAMPLE.PAS           The TP source for the main program
  277.   FSAMPLE.FOR           The MS Fortran source for the loader & routines
  278.   FSAMPLE.PAS           The dummy definitions for FSAMPLE
  279.  
  280. Also included is a Borland style MAKEFILE that compiles both parts.  
  281.  
  282. Warning:  There's a bug in MS Fortran 5.0 which means the sample program won't 
  283. run on some XT machines.  If you crash when you try to run it, read about the 
  284. problem and a patch to fix it in FORTRAN.BUG.
  285.  
  286. LIMITATIONS
  287.  
  288. As mentioned above, the TP emulator will not work in Fortran mode.  It's 
  289. probably best to compile your program with the $E- option.
  290.  
  291. I have real doubts that Fortran I/O will work properly when called from TP, 
  292. but haven't tested it enough to know for sure.
  293.  
  294. Because Fortran keeps so much data in the stack segment, you might not be able 
  295. to increase the stack size large enough.
  296.  
  297. FILES 
  298.  
  299. The following files should be included here:
  300.  
  301. MAKEFILE        A Borland style make file for the demo.  Just run MAKE.
  302. TPFORT   DOC    This file
  303. FSAMPLE  FOR    The demo Fortran source code
  304. CALLTP   OBJ    The object code to be linked to the Fortran routine
  305. FSAMPLE  PAS    The dummy definitions to access the Fortran code
  306. PSAMPLE  PAS    The demo Pascal source code
  307. FORTLINK TPU    The unit that handles the linking, compiled under TP 5.5
  308. FORTLINK DOC    The interface section from FortLink.tpu
  309. FORTRAN  BUG    Description of a bug and a patch for MS FORTRAN 5.0
  310.  
  311. COMMENTS AND QUESTIONS
  312.  
  313. Send any comments and/or bug reports to me at one of the following addresses:
  314.  
  315.   Duncan Murdoch
  316.   79 John St W
  317.   Waterloo, Ontario, Canada
  318.   N2L 1B7
  319.  
  320.   Internet:  dmurdoch@watstat.waterloo.edu
  321.   Fidonet:   dj murdoch at 1:221/180.4
  322.   Compuserve: 71631,122
  323.  
  324. SOURCE CODE 
  325.  
  326. TPFORT makes some use of the Turbo Professional library from TurboPower.  It's 
  327. a mixture of Turbo Pascal 5.5 and A86 assembler, and requires the Turbo 
  328. Professional library to recompile.  The source code can be obtained from me 
  329. for $125.  If you need the source for educational or other non-profit purposes 
  330. and this price is too high, write to me and we may be able to arrange a 
  331. discount. 
  332.  
  333. WARRANTY
  334.  
  335. There is no warranty of any kind with this program.  Use it for free; I hope 
  336. you get some value out of it.  
  337.  
  338.  
  339.          ----------------end-of-author's-documentation---------------
  340.  
  341.                         Software Library Information:
  342.  
  343.                    This disk copy provided as a service of
  344.  
  345.                         The Public (Software) Library
  346.  
  347.          We are not the authors of this program, nor are we associated
  348.          with the author in any way other than as a distributor of the
  349.          program in accordance with the author's terms of distribution.
  350.  
  351.          Please direct shareware payments and specific questions about
  352.          this program to the author of the program, whose name appears
  353.          elsewhere in  this documentation. If you have trouble getting
  354.          in touch with the author,  we will do whatever we can to help
  355.          you with your questions. All programs have been tested and do
  356.          run.  To report problems,  please use the form that is in the
  357.          file PROBLEM.DOC on many of our disks or in other written for-
  358.          mat with screen printouts, if possible.  The P(s)L cannot de-
  359.          bug programs over the telephone.
  360.  
  361.          Disks in the P(s)L are updated monthly, so if you did not get
  362.          this disk  directly from the P(s)L,  you should be aware that
  363.          the files in this set may no  longer be the current versions.
  364.  
  365.          For a copy of the latest monthly software library newsletter
  366.          and a list of the 2,000+ disks in the library, call or write
  367.  
  368.                         The Public (Software) Library
  369.                               P.O.Box 35705 - F
  370.                            Houston, TX 77235-5705
  371.                                (713) 665-7017
  372.  
  373.